---------------------------------------------------------------------------------
  -- Design Name : Instruction Decode Stage
  -- File Name   : IdStage.vhd
  -- Function    : Instruction decode stage
  -- Authors     : Mirko Francuski  2006/0225
  --               Milos Mihajlovic 2006/0039
  -- School      : University of Belgrade
  --               School for Electrical Engineering
  --               Department for Computer Engineering and Information Theory
  -- Subject     : VLSI Computer Systems
---------------------------------------------------------------------------------

library ieee;

use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.UserPkg.all;

entity IdStage is
  port (
    clk         : in  std_logic;
    wrkIn       : in  std_logic;
    wrkOut      : out std_logic;
    newPC       : in  word32;
    DIBus       : in  word32;
    wr          : in  std_logic;
    rwr         : in  regAddr;
    rwr16       : in  std_logic;
    wbData      : in  word32;
    prevOp      : in  opCode;
    prevRdAddr  : in  regAddr;
    reset       : in  std_logic;
    op          : out opCode;
    rdAddr      : out regAddr;
    rs1Addr     : out regAddr;
    rs2Addr     : out regAddr;    
    newAddr     : out word32;
    rs1Data     : out word32;
    rs2Data     : out word32;
    idle        : out std_logic;
    stall       : out std_logic;
    halt        : out std_logic
  );
end IdStage;

architecture behavioral of IdStage is
  signal instruction: word32;
  signal operation:   opCode;
  signal rd:          regAddr;
  signal rs1:         regAddr;
  signal rs2:         regAddr;
  signal imm11:       std_logic_vector(10 downto 0);

  signal addrType:    std_logic;
  signal shifts:      std_logic;
  signal immType:     std_logic_vector(1 downto 0);
  signal immUse:      std_logic;
  
  signal muxAdd:      word32;
  
  signal addV:        std_logic;
  signal addC:        std_logic;

  signal regsIn1:     regAddr;
  signal regsIn2:     regAddr;
  signal regsOut1:    word32;
  signal regsOut2:    word32;

  signal imm:         word32;
  
  signal branchCond:  std_logic;
  signal branchB:     std_logic;
  signal branchJ:     std_logic;
  signal branchTaken: std_logic;
  
  signal previousOp:  opCode;
  signal stallSignal: std_logic;
  signal haltSignal:  std_logic;
  
  signal clock:       std_logic;
begin

  /*process(clk)
  begin
    if(falling_edge(clk)) then
      if(stallSignal = '1') then
        stallSignal <= '0';
      end if;
    end if;
    if rising_edge(clk) then
      if(operation = OPC_STORE  and 
         prevOp = OPC_LOAD)      or
         rs1 = prevRdAddr        or
         rs2 = prevRdAddr      then
         stallSignal <= '1';
      else
         stallSignal <= '0';
      end if;
    end if;           
  end process;*/
  stallSignal <= '1' when wrkIn = '1' and
                          ( (operation = OPC_STORE    and 
                             prevOp = OPC_LOAD)        or                            
                             (  (branchTaken = '1'       or
                                 operation = OPC_LOAD    or
                                 operation = OPC_STORE) and
                                 prevOp /= OPC_STORE and
                                 (regsIn1 = prevRdAddr    or  
                                 regsIn2 = prevRdAddr))) else
                 '0'; 
  /*<= OPC_NOP when stallSignal else
             operation;             
  */
  op      <= operation;
  rdAddr  <= rd;    
  rs1Addr <= rs1 when shifts = '0' else
             rd;
  rs2Addr <= rs2;
  idle    <= branchTaken;
  stall   <= stallSignal;
  halt    <= haltSignal;
  
      
  rs1Data <= regsOut1;  
  regsIn2 <= rs2;
  branchTaken <= branchB or branchJ;
    
  instruction <= DIBus;
  
  /*
  stallSignal   <= '1' when (operation = OPC_STORE and 
                            prevOp = OPC_LOAD)      or
                            rs1 = prevRdAddr        or
                            rs2 = prevRdAddr      else
                   '0';
       */            
  haltSignal    <= '1' when operation = OPC_HALT else
                   '0';
  
  wrkOut <= wrkIn;
  
  spliter: IdInstrSplit port map (
    data      => instruction,
    op        => operation,
    rd        => rd,
    rs1       => rs1,
    rs2       => rs2,
    imm11     => imm11
   );
      
  expander: IdImmExp port map (
    immType   => immType,
    rd        => rd,
    rs2       => rs2,
    imm11     => imm11,
    imm       => imm
  );  
      
  jmpMux: GenMux32_2 port map (
    in1       => newPC,
    in2       => regsOut1,
    sel       => addrType,
    muxOut    => muxAdd
  );

  immMux: GenMux32_2 port map (
    in1       => regsOut2,
    in2       => imm,
    sel       => immUse,
    muxOut    => rs2Data
  );  
  
  shiftMux: IdMux5_2 port map (
    in1       => rs1,
    in2       => rd,
    sel       => shifts,
    muxOut    => regsIn1
  );
  
  adder: GenAdd32 port map ( 
    in1       => muxAdd,
    in2       => imm,
    add_out   => newAddr,
    v         => addV,
    c         => addC
  );
    
  registers: IdRegs32_32 port map (
    clk       => clk,
    rs1       => regsIn1,
    rs2       => regsIn2,
    wr        => wr,
    cl        => reset,
    rwr       => rwr,
    rwr16     => rwr16,
    rwr_data  => wbData,
    rs1_data  => regsOut1,
    rs2_data  => regsOut2
  );
  
  branchCalc: IdBranchCalc port map (
    enable    => branchCond,
    opH       => operation(5 downto 3),     
    in1       => regsOut1,
    in2       => regsOut2,
    br_cnd    => branchB
  );
  
  control: IdCtrl port map (
    op        => operation,
    addC      => addC,
    addV      => addV,
    addrType  => addrType,
    shifts    => shifts,
    immType   => immType,
    immUse    => immUse,
    branchCond=> branchCond,
    branchAlw => branchJ
  );
  
  process(clock,addV,addC)
  begin
    if(addV or addC) then
      report "Interrupt: overflow when calculating address for jump/branch instructions." severity note;
    end if;
  end process;
  
  process(clock,operation,wrkIn)
  begin
    if rising_edge(clock) then
      if (wrkIn = '1' and
         operation /= OPC_LOAD  and
         operation /= OPC_ADD   and
         operation /= OPC_AND   and
         operation /= OPC_SHL   and
         operation /= OPC_JMP   and
         operation /= OPC_BEQ   and
         operation /= OPC_HALT  and
         operation /= OPC_STORE and
         operation /= OPC_SUB   and
         operation /= OPC_OR    and
         operation /= OPC_SHR   and
         operation /= OPC_JSR   and
         operation /= OPC_BNQ   and
         operation /= OPC_XOR   and
         operation /= OPC_SAR   and
         operation /= OPC_RTS   and
         operation /= OPC_BGT   and
         operation /= OPC_NOT   and
         operation /= OPC_ROL   and
         operation /= OPC_BLT   and
         operation /= OPC_MOV   and
         operation /= OPC_ROR   and
         operation /= OPC_PUSH  and
         operation /= OPC_BGE   and
         operation /= OPC_MOVI  and
         operation /= OPC_SUBI  and
         operation /= OPC_POP   and
         operation /= OPC_BLE   and
         operation /= OPC_NOP
      ) then
        report "Interrupt: instruction unkown." severity failure;
      end if;
    end if;
  end process;
 
end architecture behavioral;